package com.example.demo.retry;

import com.github.rholder.retry.Retryer;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;

/**
 * @author zt22328
 */
//@Aspect
//@Component
@Slf4j
public class RetryAspect {
	@Autowired
	ISysMethodRetryLogService sysMethodRetryLogService;
	@Autowired
	Retryer retryer;
	
	@Pointcut("@annotation(RetryLog)")
	public void retryPointCut() { 

	}

	@Around("retryPointCut()")
	public Object proceed(ProceedingJoinPoint point) throws Throwable {
		Object result = null;
		Throwable ex = null;
		try {
			result = point.proceed();
		} catch (Throwable e) {
			ex = e;
			if(getRetrySuccess(point)){
				return null;
			}
			throw e;
		}finally {
			retry(point,result,ex);
		}
		return result;
	}

	public boolean getRetrySuccess(ProceedingJoinPoint point){
		MethodSignature signature = (MethodSignature) point.getSignature();
		Method method = signature.getMethod();
		RetryLog log = method.getAnnotation(RetryLog.class);
		return log.success();
	}

	public void retry(ProceedingJoinPoint point,Object result,Throwable ex){

		boolean retryFlag = retryLog(point, result, ex);
		if(retryFlag){
			String kongEnv = KongAdminContext.KongEnv.get();
			Long kongPId = KongAdminContext.KongPrimaryTid.get();
			Long kongTId = KongAdminContext.KongTid.get();
			CompletableFuture.runAsync(()->{
				try {
					retryer.call(()->{
						try {
							KongAdminContext.KongEnv.set(kongEnv);
							KongAdminContext.KongPrimaryTid.set(kongPId);
							KongAdminContext.KongTid.set(kongTId);
							return this.proceed2(point);
						} catch (Throwable throwable) {
							log.error("重试异常",throwable);
							throw new Exception(throwable);
						}finally {
							KongAdminContext.KongEnv.remove();
							KongAdminContext.KongPrimaryTid.remove();
							KongAdminContext.KongTid.remove();
						}
					});
				} catch (Exception e) {
					log.error("重试异常",e);
				}
			});
		}
	}

	private boolean retryLog(ProceedingJoinPoint point, Object result, Throwable ex) {
		try {
			return sysMethodRetryLogService.retryLog(point, result, ex);
		}catch (Exception e){
			log.error("重试记录异常！",e);
			return false;
		}
	}

	public Object proceed2(ProceedingJoinPoint point) throws Throwable {
		Object result = null;
		Throwable ex = null;
		try {
			result = point.proceed();
		} catch (Throwable e) {
			ex = e;
			throw e;
		}finally {
			retryLog(point, result, ex);
		}
		return result;
	}
}
